package org.ehe.business.step.pdf;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.file.Files;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 优化的3D模型预览渲染器 - 解决超时问题
 * 主要优化：
 * 1. 使用FreeCADCmd无头模式
 * 2. 简化渲染脚本，减少GUI依赖
 * 3. 分阶段超时控制
 * 4. 增强进程管理和监控
 * 5. 增加预检查和快速失败机制
 */
@Slf4j
@Component
public class Render3DModelPreview {

    private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/step_previews";
    private static final int QUICK_CHECK_TIMEOUT = 10; // 10秒快速检查
    private static final int RENDER_TIMEOUT = 90; // 90秒渲染超时
    private static final int MAX_FILE_SIZE_MB = 50; // 最大文件大小限制
    private static final ExecutorService executorService = Executors.newCachedThreadPool(r -> {
        Thread t = new Thread(r, "FreeCAD-Render-Thread");
        t.setDaemon(true);
        return t;
    });

    public String modelPreview(File stepFile) {
        // 预检查
        if (!preCheckFile(stepFile)) {
            return generateFallbackImage(stepFile, ensureTempDir(), UUID.randomUUID().toString());
        }

        String uuid = UUID.randomUUID().toString();
        File tempDir = ensureTempDir();
        File outputImage = new File(tempDir, uuid + "_preview.png");
        File logFile = new File(tempDir, uuid + "_render.log");

        try {
            // 先尝试快速简单渲染
            if (quickRender(stepFile, outputImage, logFile, uuid)) {
                log.info("快速渲染成功: {}, 大小: {} bytes",
                    outputImage.getAbsolutePath(), outputImage.length());
                return outputImage.getAbsolutePath();
            }

            // 快速渲染失败，尝试完整渲染
            log.info("快速渲染失败，尝试完整渲染");
            if (fullRender(stepFile, outputImage, logFile, uuid)) {
                log.info("完整渲染成功: {}, 大小: {} bytes",
                    outputImage.getAbsolutePath(), outputImage.length());
                return outputImage.getAbsolutePath();
            }

            // 所有渲染都失败，生成占位图
            log.warn("所有渲染方式都失败，生成占位图");
            logRenderError(logFile);
            return generateFallbackImage(stepFile, tempDir, uuid);

        } catch (Exception e) {
            log.error("渲染3D模型时出错: " + stepFile.getName(), e);
            return generateFallbackImage(stepFile, tempDir, uuid);
        }
    }

    /**
     * 预检查文件
     */
    private boolean preCheckFile(File stepFile) {
        if (!stepFile.exists() || !stepFile.canRead()) {
            log.warn("文件不存在或不可读: {}", stepFile.getAbsolutePath());
            return false;
        }

        long fileSizeMB = stepFile.length() / (1024 * 1024);
        if (fileSizeMB > MAX_FILE_SIZE_MB) {
            log.warn("文件过大 ({}MB > {}MB): {}", fileSizeMB, MAX_FILE_SIZE_MB, stepFile.getName());
            return false;
        }

        // 检查文件格式
        String fileName = stepFile.getName().toLowerCase();
        if (!fileName.endsWith(".step") && !fileName.endsWith(".stp")) {
            log.warn("不支持的文件格式: {}", fileName);
            return false;
        }

        return true;
    }

    /**
     * 快速简单渲染 - 无GUI模式
     */
    private boolean quickRender(File stepFile, File outputImage, File logFile, String uuid) {
        File scriptFile = new File(outputImage.getParentFile(), uuid + "_quick.py");

        try {
            String script = createQuickRenderScript(stepFile, outputImage, logFile);
            Files.write(scriptFile.toPath(), script.getBytes());

            return executeRenderWithTimeout(scriptFile, logFile, QUICK_CHECK_TIMEOUT, outputImage);
        } catch (Exception e) {
            log.debug("快速渲染异常", e);
            return false;
        } finally {
            safeDelete(scriptFile);
        }
    }

    /**
     * 完整渲染 - 带GUI支持
     */
    private boolean fullRender(File stepFile, File outputImage, File logFile, String uuid) {
        File scriptFile = new File(outputImage.getParentFile(), uuid + "_full.py");

        try {
            String script = createFullRenderScript(stepFile, outputImage, logFile);
            Files.write(scriptFile.toPath(), script.getBytes());

            return executeRenderWithTimeout(scriptFile, logFile, RENDER_TIMEOUT, outputImage);
        } catch (Exception e) {
            log.debug("完整渲染异常", e);
            return false;
        } finally {
            safeDelete(scriptFile);
        }
    }

    /**
     * 创建快速渲染脚本 - 专注于速度
     */
    private String createQuickRenderScript(File stepFile, File outputImage, File logFile) {
        return String.format("""
                import FreeCAD
                import sys
                import os
                import traceback
                from datetime import datetime

                def log_msg(msg):
                    try:
                        with open('%s', 'a', encoding='utf-8') as f:
                            f.write(f'[{datetime.now()}] {msg}\\n')
                            f.flush()
                    except: pass

                try:
                    log_msg('=== 快速渲染开始 ===')

                    step_file = '%s'
                    output_file = '%s'

                    if not os.path.exists(step_file):
                        log_msg('文件不存在')
                        sys.exit(1)

                    # 无头模式导入
                    log_msg('创建文档')
                    doc = FreeCAD.newDocument('QuickRender')

                    log_msg('导入STEP文件')
                    import Import
                    Import.insert(step_file, doc.Name)

                    if len(doc.Objects) == 0:
                        log_msg('未找到3D对象')
                        sys.exit(1)

                    log_msg(f'导入 {len(doc.Objects)} 个对象')

                    # 尝试无GUI导出
                    try:
                        # 计算包围盒
                        bbox = None
                        for obj in doc.Objects:
                            if hasattr(obj, 'Shape') and obj.Shape.BoundBox.isValid():
                                if bbox is None:
                                    bbox = obj.Shape.BoundBox
                                else:
                                    bbox.add(obj.Shape.BoundBox)

                        if bbox:
                            log_msg(f'包围盒: {bbox.XLength:.1f} x {bbox.YLength:.1f} x {bbox.ZLength:.1f}')

                            # 使用Draft模块的SVG导出然后转换
                            try:
                                import Draft
                                svg_file = output_file.replace('.png', '.svg')
                                Draft.getSVG(doc.Objects, direction=FreeCAD.Vector(1,1,1))
                                log_msg('SVG导出尝试完成')
                            except Exception as e:
                                log_msg(f'SVG导出失败: {e}')

                        # 如果有GUI支持，尝试简单截图
                        try:
                            import FreeCADGui
                            if FreeCADGui.activeDocument():
                                view = FreeCADGui.activeView()
                                if view:
                                    view.viewIsometric()
                                    view.fitAll()
                                    view.saveImage(output_file, 512, 384, 'White')
                                    log_msg('GUI截图成功')
                        except:
                            log_msg('GUI截图失败，使用备用方案')

                    except Exception as e:
                        log_msg(f'导出异常: {e}')

                    # 检查输出文件
                    if os.path.exists(output_file) and os.path.getsize(output_file) > 100:
                        log_msg('快速渲染成功')
                        sys.exit(0)
                    else:
                        log_msg('快速渲染失败')
                        sys.exit(1)

                except Exception as e:
                    log_msg(f'快速渲染异常: {e}')
                    log_msg(traceback.format_exc())
                    sys.exit(2)
                finally:
                    try:
                        if 'doc' in locals():
                            FreeCAD.closeDocument(doc.Name)
                    except: pass
                """,
            logFile.getAbsolutePath().replace("\\", "/"),
            stepFile.getAbsolutePath().replace("\\", "/"),
            outputImage.getAbsolutePath().replace("\\", "/"));
    }

    /**
     * 创建完整渲染脚本 - 更完整的功能
     */
    private String createFullRenderScript(File stepFile, File outputImage, File logFile) {
        return String.format("""
                import FreeCAD
                import sys
                import os
                import time
                from datetime import datetime

                def log_msg(msg):
                    try:
                        with open('%s', 'a', encoding='utf-8') as f:
                            f.write(f'[{datetime.now()}] {msg}\\n')
                            f.flush()
                    except: pass

                try:
                    log_msg('=== 完整渲染开始 ===')

                    step_file = '%s'
                    output_file = '%s'

                    # 创建文档并导入
                    doc = FreeCAD.newDocument('FullRender')
                    import Import
                    Import.insert(step_file, doc.Name)

                    if len(doc.Objects) == 0:
                        log_msg('未找到对象')
                        sys.exit(1)

                    log_msg(f'导入 {len(doc.Objects)} 个对象')

                    # 启用GUI（如果可能）
                    gui_available = False
                    try:
                        import FreeCADGui
                        if not FreeCADGui.activeDocument():
                            FreeCADGui.activateDocument(doc.Name)
                        gui_available = True
                        log_msg('GUI可用')

                        # 设置视图
                        view = FreeCADGui.activeView()
                        if view:
                            # 显示所有对象
                            for obj in doc.Objects:
                                if hasattr(obj, 'ViewObject'):
                                    obj.ViewObject.Visibility = True

                            # 设置视图参数
                            view.viewIsometric()
                            time.sleep(0.5)  # 给视图更新时间
                            view.fitAll()
                            time.sleep(0.5)

                            # 设置渲染质量
                            try:
                                view.setBackgroundColor((1.0, 1.0, 1.0, 1.0))
                            except: pass

                            # 导出图像
                            width, height = 800, 600
                            view.saveImage(output_file, width, height, 'White')
                            log_msg(f'导出 {width}x{height} 图像')

                    except Exception as e:
                        log_msg(f'GUI渲染失败: {e}')
                        gui_available = False

                    # 检查结果
                    if os.path.exists(output_file) and os.path.getsize(output_file) > 1000:
                        log_msg(f'渲染成功，文件大小: {os.path.getsize(output_file)} bytes')
                        sys.exit(0)
                    else:
                        log_msg('渲染失败')
                        sys.exit(1)

                except Exception as e:
                    log_msg(f'完整渲染异常: {str(e)}')
                    import traceback
                    log_msg(traceback.format_exc())
                    sys.exit(2)
                finally:
                    try:
                        if 'doc' in locals():
                            FreeCAD.closeDocument(doc.Name)
                    except: pass
                """,
            logFile.getAbsolutePath().replace("\\", "/"),
            stepFile.getAbsolutePath().replace("\\", "/"),
            outputImage.getAbsolutePath().replace("\\", "/"));
    }

    /**
     * 执行渲染命令，支持超时控制
     */
    private boolean executeRenderWithTimeout(File scriptFile, File logFile, int timeoutSeconds, File expectedOutput) {
        Future<Boolean> future = executorService.submit(() -> {
            try {
                String[] command = buildOptimizedRenderCommand(scriptFile);
                log.debug("执行命令: {}", String.join(" ", command));

                ProcessBuilder pb = new ProcessBuilder(command);
                pb.directory(scriptFile.getParentFile());
                pb.redirectErrorStream(true);

                // 设置环境变量，减少FreeCAD启动时间
                pb.environment().put("FREECAD_HEADLESS", "1");
                pb.environment().put("QT_QPA_PLATFORM", "offscreen");

                Process process = pb.start();

                // 监控进程输出
                AtomicBoolean outputComplete = new AtomicBoolean(false);
                Thread outputMonitor = new Thread(() -> {
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                        String line;
                        while ((line = reader.readLine()) != null && !Thread.currentThread().isInterrupted()) {
                            log.debug("FreeCAD: {}", line);
                        }
                    } catch (IOException e) {
                        log.debug("输出监控异常", e);
                    } finally {
                        outputComplete.set(true);
                    }
                });
                outputMonitor.start();

                // 等待进程完成
                boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);

                if (!finished) {
                    log.warn("进程超时 ({}s)，终止进程", timeoutSeconds);
                    process.destroyForcibly();
                    process.waitFor(5, TimeUnit.SECONDS); // 等待强制终止完成
                    return false;
                }

                outputMonitor.interrupt();
                int exitCode = process.exitValue();
                log.debug("进程退出码: {}", exitCode);

                // 检查输出文件
                boolean success = exitCode == 0 && expectedOutput.exists() && expectedOutput.length() > 100;
                log.debug("渲染{}，输出文件存在: {}, 大小: {}",
                    success ? "成功" : "失败",
                    expectedOutput.exists(),
                    expectedOutput.exists() ? expectedOutput.length() : 0);

                return success;

            } catch (Exception e) {
                log.debug("执行渲染异常", e);
                return false;
            }
        });

        try {
            return future.get(timeoutSeconds + 5, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.warn("渲染任务整体超时");
            future.cancel(true);
            return false;
        } catch (Exception e) {
            log.debug("渲染任务异常", e);
            return false;
        }
    }

    /**
     * 构建优化的渲染命令
     */
    private String[] buildOptimizedRenderCommand(File scriptFile) {
        String os = System.getProperty("os.name").toLowerCase();

        if (os.contains("win")) {
            // Windows - 优先使用命令行版本
            return new String[]{
                "cmd", "/c", "freecadcmd", "-c", scriptFile.getAbsolutePath()
            };
        } else if (os.contains("mac")) {
            // macOS - 使用命令行版本
            return new String[]{
                "/Applications/FreeCAD.app/Contents/MacOS/FreeCAD",
                "-c", scriptFile.getAbsolutePath()
            };
        } else {
            // Linux - 优先使用命令行版本
            return new String[]{
                "freecadcmd", "-c", scriptFile.getAbsolutePath()
            };
        }
    }

    /**
     * 确保临时目录存在
     */
    private File ensureTempDir() {
        File tempDir = new File(TEMP_DIR);
        if (!tempDir.exists()) {
            tempDir.mkdirs();
            log.debug("创建临时目录: {}", tempDir.getAbsolutePath());
        }
        return tempDir;
    }

    /**
     * 安全删除文件
     */
    private void safeDelete(File file) {
        try {
            if (file != null && file.exists()) {
                file.delete();
            }
        } catch (Exception e) {
            log.debug("删除文件失败: {}", file, e);
        }
    }

    private void logRenderError(File logFile) {
        if (logFile.exists()) {
            try {
                String logContent = Files.readString(logFile.toPath());
                log.error("渲染日志:\n{}", logContent);
            } catch (IOException e) {
                log.error("无法读取日志文件", e);
            }
        }
    }

    /**
     * 生成占位图像 - 改进版本
     */
    private String generateFallbackImage(File stepFile, File tempDir, String uuid) {
        try {
            File fallbackImage = new File(tempDir, uuid + "_fallback.png");

            java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(800, 600, java.awt.image.BufferedImage.TYPE_INT_RGB);
            java.awt.Graphics2D g2d = image.createGraphics();

            // 抗锯齿
            g2d.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            // 渐变背景
            java.awt.GradientPaint gradient = new java.awt.GradientPaint(0, 0, new java.awt.Color(240, 240, 240), 0, 600, java.awt.Color.WHITE);
            g2d.setPaint(gradient);
            g2d.fillRect(0, 0, 800, 600);

            // 3D立方体图标
            g2d.setColor(new java.awt.Color(100, 150, 200, 180));
            int[] xPoints1 = {300, 400, 450, 350};
            int[] yPoints1 = {180, 180, 130, 130};
            g2d.fillPolygon(xPoints1, yPoints1, 4);

            g2d.setColor(new java.awt.Color(80, 120, 180, 180));
            int[] xPoints2 = {400, 500, 550, 450};
            int[] yPoints2 = {180, 180, 130, 130};
            g2d.fillPolygon(xPoints2, yPoints2, 4);

            g2d.setColor(new java.awt.Color(60, 100, 160, 180));
            int[] xPoints3 = {400, 500, 500, 400};
            int[] yPoints3 = {180, 180, 280, 280};
            g2d.fillPolygon(xPoints3, yPoints3, 4);

            // 边框
            g2d.setColor(new java.awt.Color(50, 80, 140));
            g2d.setStroke(new java.awt.BasicStroke(2));
            g2d.drawPolygon(xPoints1, yPoints1, 4);
            g2d.drawPolygon(xPoints2, yPoints2, 4);
            g2d.drawPolygon(xPoints3, yPoints3, 4);

            // 标题
            g2d.setColor(new java.awt.Color(50, 50, 50));
            g2d.setFont(new java.awt.Font("Arial", java.awt.Font.BOLD, 28));
            String title = "STEP 3D模型预览";
            java.awt.FontMetrics fm = g2d.getFontMetrics();
            int titleX = (800 - fm.stringWidth(title)) / 2;
            g2d.drawString(title, titleX, 340);

            // 文件信息
            g2d.setFont(new java.awt.Font("Arial", java.awt.Font.PLAIN, 16));
            g2d.setColor(new java.awt.Color(80, 80, 80));

            String fileName = "文件: " + stepFile.getName();
            String fileSize = "大小: " + formatFileSize(stepFile.length());
            String status = "状态: 3D渲染服务暂时不可用";
            String suggestion = "建议: 请检查FreeCAD安装或稍后重试";

            int y = 380;
            g2d.drawString(fileName, 50, y);
            g2d.drawString(fileSize, 50, y + 25);
            g2d.drawString(status, 50, y + 50);
            g2d.drawString(suggestion, 50, y + 75);

            // 时间戳
            g2d.setFont(new java.awt.Font("Arial", java.awt.Font.PLAIN, 12));
            g2d.setColor(new java.awt.Color(150, 150, 150));
            g2d.drawString("生成时间: " + new java.util.Date(), 50, 550);

            g2d.dispose();

            // 保存
            javax.imageio.ImageIO.write(image, "PNG", fallbackImage);

            log.info("生成占位图: {}", fallbackImage.getAbsolutePath());
            return fallbackImage.getAbsolutePath();

        } catch (Exception e) {
            log.error("生成占位图失败", e);
            return null;
        }
    }

    private String formatFileSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
        return String.format("%.1f GB", bytes / (1024.0 * 1024.0 * 1024.0));
    }

    /**
     * 检查FreeCAD可用性 - 优化版本
     */
    public boolean isFreeCADAvailable() {
        try {
            String[] commands = {
                "freecadcmd --version",    // 命令行版本优先
                "freecad --version"        // GUI版本备用
            };

            for (String cmd : commands) {
                try {
                    ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
                    Process process = pb.start();
                    boolean finished = process.waitFor(10, TimeUnit.SECONDS);

                    if (finished && process.exitValue() == 0) {
                        log.info("FreeCAD可用: {}", cmd);
                        return true;
                    }
                } catch (Exception e) {
                    log.debug("检查命令失败: {}", cmd, e);
                }
            }

            log.warn("FreeCAD不可用");
            return false;
        } catch (Exception e) {
            log.warn("FreeCAD可用性检查异常", e);
            return false;
        }
    }

    /**
     * 清理临时文件 - 优化版本
     */
    public void cleanupTempFiles() {
        CompletableFuture.runAsync(() -> {
            try {
                File tempDir = new File(TEMP_DIR);
                if (!tempDir.exists()) return;

                File[] files = tempDir.listFiles();
                if (files == null) return;

                long cutoffTime = System.currentTimeMillis() - 6 * 60 * 60 * 1000; // 6小时前
                int cleanedCount = 0;
                long freedSpace = 0;

                for (File file : files) {
                    if (file.lastModified() < cutoffTime) {
                        long fileSize = file.length();
                        if (file.delete()) {
                            cleanedCount++;
                            freedSpace += fileSize;
                        }
                    }
                }

                if (cleanedCount > 0) {
                    log.info("清理了 {} 个临时文件，释放空间 {}", cleanedCount, formatFileSize(freedSpace));
                }
            } catch (Exception e) {
                log.warn("清理临时文件失败", e);
            }
        }, executorService);
    }

    /**
     * 关闭线程池
     */
    public void shutdown() {
        try {
            executorService.shutdown();
            if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}
